package com.yonyou.pmclouds.project.rmiimpl;

import com.alibaba.dubbo.config.annotation.Service;
import com.yonyou.pmclouds.ApplicationConsts;
import com.yonyou.pmclouds.basecom.bp.processor.AuditInfoInsertProcessor;
import com.yonyou.pmclouds.basecom.bp.processor.AuditInfoUpdateProcessor;
import com.yonyou.pmclouds.basecom.bp.processor.BdReferenceChecker;
import com.yonyou.pmclouds.basecom.bp.template.SingleDeleteBpTemplate;
import com.yonyou.pmclouds.basecom.bp.template.SingleInsertBpTemplate;
import com.yonyou.pmclouds.basecom.bp.template.SingleUpdateBpTemplate;
import com.yonyou.pmclouds.basecom.exception.BusinessException;
import com.yonyou.pmclouds.basecom.ref.itf.RefCodeConst;
import com.yonyou.pmclouds.basecom.ref.itf.ReferenceChecker;
import com.yonyou.pmclouds.basecom.runtime.RuntimeEnvironment;
import com.yonyou.pmclouds.basecom.util.BeanHelper;
import com.yonyou.pmclouds.basecom.util.CommonFieldConst;
import com.yonyou.pmclouds.procedure.entity.ProceprojectVO;
import com.yonyou.pmclouds.procedure.mapper.ProceprojectMapper;
import com.yonyou.pmclouds.project.entity.ProjectUpdateViewVO;
import com.yonyou.pmclouds.project.entity.ProjectVO;
import com.yonyou.pmclouds.project.entity.ProjectViewVO;
import com.yonyou.pmclouds.project.mapper.ProjectMapper;
import com.yonyou.pmclouds.project.mapper.ProjectStatisticsMapper;
import com.yonyou.pmclouds.project.processor.*;
import com.yonyou.pmclouds.project.rmiitf.ProjectMaintain;
import com.yonyou.pmclouds.project.rmiitf.ProjectSimpleQuery;
import com.yonyou.pmclouds.projectstatus.entity.ProjectStatusVO;
import com.yonyou.pmclouds.projectstatus.rmiitf.ProjectStatusQuery;
import com.yonyou.pmclouds.projectstatus.util.ProjectStatusUtils;
import com.yonyou.pmclouds.team.entity.TeamEnterpriseVO;
import com.yonyou.pmclouds.team.entity.TeamEnterpriseViewVO;
import com.yonyou.pmclouds.team.entity.TeamMemberVO;
import com.yonyou.pmclouds.team.entity.TeamMemberViewVO;
import com.yonyou.pmclouds.team.rmiimpl.TeamMaintainForProject;
import com.yonyou.pmclouds.team.rmiitf.TeamEnterpriseMaintain;
import com.yonyou.pmclouds.team.rmiitf.TeamMemberMaintain;
import com.yonyou.pmclouds.team.rmiitf.TeamMemberQuery;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;

import java.util.*;

@Slf4j
@Service(version = ApplicationConsts.APPLICATION_VERSION, interfaceClass = ProjectMaintain.class)
public class ProjectMaintainImpl implements ProjectMaintain {
    // 项目编辑更新的字段，只有项目描述可以更新
    private static final String[] UPDATE_FIELDS = {CommonFieldConst.MODIFIER, CommonFieldConst.MODIFYTIME, ProjectVO.DESCRIPTION, ProjectVO.MANAGER, ProjectVO.SCODE, ProjectVO.SNAME,
            ProjectVO.APPROVAL_DATE, ProjectVO.PROJECT_STATE, ProjectVO.ISKEY, ProjectVO.ADDRESS, ProjectVO.LONGITUDE, ProjectVO.LATITUDE};
    private static final String[] END_FIELDS = {ProjectVO.END_DATE, ProjectVO.PROJECT_STATE, ProjectVO.DESCRIPTION, CommonFieldConst.MODIFIER, CommonFieldConst.MODIFYTIME,};
    @Autowired
    private ProjectMapper projectMapper;
    @Autowired
    private ProjectStatisticsMapper statisticsMapper;
    @Autowired
    private TeamMaintainForProject teamMemberMaintain;
    @Autowired
    private ReferenceChecker referenceChecker;
    @Autowired
    private ProjectSimpleQuery projectSimpleQuery;
    @Autowired
    private TeamEnterpriseMaintain teamEnterpriseMaintain;
    @Autowired
    private TeamMemberMaintain memberMaintain;
    @Autowired
    private TeamMemberQuery memberQuery;
    @Lazy
    @Autowired
    private ProjectStatusQuery statusQuery;
    @Autowired
    private ProceprojectMapper proceprojectMapper;


    @Override
    public String insert(ProjectVO project) throws BusinessException {
        SingleInsertBpTemplate<ProjectVO> bp = new SingleInsertBpTemplate<>(projectMapper);
        // 校验编码、名称重复
        bp.addBeforeProcessor(new ProjectUniqueCheck(projectMapper));
        // 更新审计信息
        bp.addBeforeProcessor(new AuditInfoInsertProcessor<ProjectVO>());
        // 生成统计信息
        bp.addAfterProcessor(new ProjectInitStatisticsProcessor(statisticsMapper));
        bp.insert(project);
        return project.getPkProject();
    }

    //添加工序与项目的关系
    private ProceprojectVO insert(ProceprojectVO proceprojectVO) throws BusinessException{
        SingleInsertBpTemplate<ProceprojectVO> bp = new SingleInsertBpTemplate<ProceprojectVO>(proceprojectMapper);
        bp.addBeforeProcessor(new AuditInfoInsertProcessor<ProceprojectVO>());
        bp.insert(proceprojectVO);
        return proceprojectVO;
    }
    @Override
    public String update(ProjectVO project) throws BusinessException {
        SingleUpdateBpTemplate<ProjectVO> bp = new SingleUpdateBpTemplate<>(projectMapper, UPDATE_FIELDS);
        // 项目状态check
        bp.addBeforeProcessor(new ProjectUpdateCheck(RuntimeEnvironment.getLoginUserId()));
        // 更新审计信息
        bp.addBeforeProcessor(new AuditInfoUpdateProcessor<ProjectVO>());
        bp.addAfterProcessor(new EndUpdateTeamProcessor(teamMemberMaintain));
        bp.update(project);
        return project.getPkProject();
    }

    @Override
    public String updateEnd(String pkProject, String changeFlag, Date endDate, String operator, String description) throws BusinessException {
        ProjectVO tmpVO = new ProjectVO();
        tmpVO.setEndDate(endDate);
        tmpVO.setPkProject(pkProject);
        tmpVO.setDescription(description);
        tmpVO.setChangeFlag(changeFlag);
        SingleUpdateBpTemplate<ProjectVO> bp = new SingleUpdateBpTemplate<>(projectMapper, END_FIELDS);
        // 校验项目
        bp.addBeforeProcessor(new ProjectEndCheck(operator));
        // 更新审计信息
        bp.addBeforeProcessor(new AuditInfoUpdateProcessor<ProjectVO>());
        bp.addAfterProcessor(new EndUpdateTeamProcessor(teamMemberMaintain));
        bp.update(tmpVO);
        return pkProject;
    }

    @Override
    public String updatePreWorkResumeDate(String pkProject, Date preWorkResumeDate,Date stopDate) throws BusinessException {
        ProjectVO project = projectMapper.getById(pkProject);
        project = (ProjectVO) project.clone();
        project.setPreworkresumeDate(preWorkResumeDate);
        //计算需要
        project.setWorkstopDate(stopDate);
        projectMapper.updateFields(project,new String[]{ProjectVO.PREWORKRESUME_DATE,ProjectVO.WORKSTOP_DATE});
        return pkProject;
    }

    @Override
    public String updateEndDateForAcceptance(String pkProject, Date acceptDate) throws BusinessException {
        // FIXME 这里先不加锁，因为某些情况下，这个回调会被先执行，会出现某些问题，而这些字段不是用户编辑的，所以暂时放弃加锁
        ProjectVO project = projectMapper.getById(pkProject);
        project = (ProjectVO) project.clone();
        project.setEndDate(acceptDate);
//        SingleUpdateBpTemplate<ProjectVO> bp = new SingleUpdateBpTemplate<>(projectMapper, new String[]{ProjectVO.END_DATE});
//        bp.update(project);
        projectMapper.updateFields(project, new String[]{ProjectVO.END_DATE});
        return pkProject;
    }

    @Override
    public String updateForNCBillDriven(String pkProject, String pkSrcStatus) throws BusinessException {
        if (StringUtils.isEmpty(pkSrcStatus)) {
            return pkProject;
        }
        ProjectStatusVO projectStatusVO = statusQuery.queryByPkSrc(pkSrcStatus);
        if (projectStatusVO == null) {
            log.error("project status not sync, the key is {}", pkSrcStatus);
            return pkProject;
        }
        ProjectVO orig = projectMapper.getById(pkProject);
        ProjectVO project = (ProjectVO) orig.clone(); // mybatis的缓存查询条件一样，返回一个对象
        project.setPkProjectStatus(projectStatusVO.getPkProjectStatus());
        project.setProjectState(ProjectStatusUtils.getTransStatus(projectStatusVO));
        // FIXME 这里先不加锁，因为某些情况下，这个回调会被先执行，会出现某些问题，而这些字段不是用户编辑的，所以暂时放弃加锁
//        SingleUpdateBpTemplate<ProjectVO> bp = new SingleUpdateBpTemplate<>(projectMapper, new String[]{ProjectVO.PROJECT_STATE, ProjectVO.PK_PROJECT_STATUS});
//        bp.addAfterProcessor(new EndUpdateTeamProcessor(teamMemberMaintain));
//        bp.update(project);
        projectMapper.updateFields(project, new String[]{ProjectVO.PROJECT_STATE, ProjectVO.PK_PROJECT_STATUS});
        new EndUpdateTeamProcessor(teamMemberMaintain).process(project, orig);
        return pkProject;
    }

    @Override
    public String delete(String pkProject, String changeFlag, String operator) throws BusinessException {
        ProjectVO tmpVO = new ProjectVO();
        tmpVO.setPkProject(pkProject);
        tmpVO.setChangeFlag(changeFlag);
        SingleDeleteBpTemplate<ProjectVO> bp = new SingleDeleteBpTemplate<>(projectMapper);
        bp.addBeforeProcessor(new ProjectDeleteCheck());


        //删除工序和项目的关联
        bp.addBeforeProcessor(new ProceProjectDeleteProcessor(proceprojectMapper));

        bp.addBeforeProcessor(new BdReferenceChecker<ProjectVO>(RefCodeConst.PROJECT, referenceChecker));
        bp.addBeforeProcessor(new AuditInfoUpdateProcessor<ProjectVO>());
        bp.addAfterProcessor(new DeleteUpdateTeamProcessor(teamMemberMaintain));
        bp.delete(tmpVO);
        return pkProject;
    }

    @Override
    public ProjectViewVO insert(ProjectViewVO projectViewVO) throws BusinessException {

        ProjectVO projectVO = genProjectVO(projectViewVO);
        String pkProject = this.insert(projectVO);

        //添加工序与项目的关系
        if (projectVO.getProcedures()!=null&&projectVO.getProcedures().length!=0){
            for (String procedure:projectVO.getProcedures()){
                ProceprojectVO proceprojectVO = new ProceprojectVO();
                proceprojectVO.setPkProcedure(procedure);
                proceprojectVO.setPkProject(pkProject);
                this.insert(proceprojectVO);
            }
        }

        projectViewVO.setPkProject(pkProject);

        TeamEnterpriseViewVO[] teamEnterpriseViewVOs = projectViewVO.getTeamEnterpriseViewVOs();

        if (ArrayUtils.isEmpty(teamEnterpriseViewVOs))
            return projectViewVO;

        Map<String, TeamEnterpriseVO> entMap = new HashMap<>();
        List<TeamMemberVO> teamMemberVOList = new ArrayList<>();
        genEnterAndMemberVOs(teamEnterpriseViewVOs, entMap, teamMemberVOList, pkProject);

        TeamEnterpriseVO[] teamEnterpriseVOs = entMap.values().toArray(new TeamEnterpriseVO[0]);
        if (ArrayUtils.isNotEmpty(teamEnterpriseVOs)) {
            teamEnterpriseMaintain.addEnterpriseToProject(teamEnterpriseVOs[0].getPkProject(), teamEnterpriseVOs);
            if (teamMemberVOList.size() > 0) {

                fillMemberVOList(teamMemberVOList, entMap, projectVO);

                memberMaintain.addMembersToProject(teamMemberVOList.get(0).getPkProject(), teamMemberVOList.toArray(new TeamMemberVO[0]));
            }
        }
        return projectViewVO;
    }

    private void fillMemberVOList(List<TeamMemberVO> teamMemberVOList, Map<String, TeamEnterpriseVO> entMap, ProjectVO projectVO) throws BusinessException {

        for (TeamMemberVO teamMemberVO :
                teamMemberVOList) {
            teamMemberVO.setPkTenant(RuntimeEnvironment.getTenantId());
            teamMemberVO.setPkProject(projectVO.getPkProject());
            TeamEnterpriseVO ent = entMap.get(teamMemberVO.getPkEnterprise());
            if (ent != null) {
                if (StringUtils.isNotEmpty(projectVO.getManager()) && projectVO.getManager().equals(teamMemberVO.getPkUser())) {
                    teamMemberVO.setUserRole(TeamMemberVO.UserRoleConst.ROLE_MANAGER);
                } else {
                    teamMemberVO.setUserRole(ent.getEntRole());
                }
            } else {
                BusinessException ex = new BusinessException();
                ex.addInfo("userNotInEnt", teamMemberVO.getPkUser());
                throw ex;
            }

        }
    }

    private ProjectVO genProjectVO(ProjectViewVO projectViewVO) {
        ProjectVO projectVO = new ProjectVO();
        if (projectViewVO == null)
            return projectVO;
        for (String field : ProjectViewVO.transFields) {
            BeanHelper.setProperty(projectVO, field, BeanHelper.getProperty(projectViewVO, field));
        }
        return projectVO;
    }

    private void genEnterAndMemberVOs(TeamEnterpriseViewVO[] teamEnterpriseViewVOs, Map<String, TeamEnterpriseVO> entMap, List<TeamMemberVO> teamMemberVOList, String pkProject) {

        if (ArrayUtils.isEmpty(teamEnterpriseViewVOs))
            return;

        for (int i = 0; i < teamEnterpriseViewVOs.length; i++) {
            TeamEnterpriseVO teamEnterpriseVO = new TeamEnterpriseVO();
            for (String field :
                    TeamEnterpriseViewVO.transFields) {
                BeanHelper.setProperty(teamEnterpriseVO, field, BeanHelper.getProperty(teamEnterpriseViewVOs[i], field));
                teamEnterpriseVO.setPkProject(pkProject);
            }
            entMap.put(teamEnterpriseVO.getPkEnterprise(), teamEnterpriseVO);

            TeamMemberViewVO[] teamMemberViewVOs = teamEnterpriseViewVOs[i].getTeamMemberViewVOs();
            if (ArrayUtils.isNotEmpty(teamMemberViewVOs)) {

                for (TeamMemberViewVO teamMemberViewVO :
                        teamMemberViewVOs) {

                    TeamMemberVO teamMemberVO = new TeamMemberVO();
                    for (String field :
                            TeamMemberViewVO.transFields) {
                        BeanHelper.setProperty(teamMemberVO, field, BeanHelper.getProperty(teamMemberViewVO, field));
                        teamMemberVO.setPkProject(pkProject);
                    }
                    teamMemberVOList.add(teamMemberVO);
                }

            }

        }


    }

    @Override
    public String updateProjectInfo(ProjectUpdateViewVO projectUpdateViewVO) throws BusinessException {

        ProjectVO projectVO = projectUpdateViewVO.getProject();

        if (projectVO == null)
            return null;

        String pkProject = projectVO.getPkProject();
        this.update(projectVO);

        updateTeamEnter(pkProject, projectUpdateViewVO);

        updateTeamMember(pkProject, projectUpdateViewVO);

        //更新关联
        updateProcedure(pkProject,projectVO.getProcedures());

        checkMangerInTeam(projectVO);

        return pkProject;
    }

    private void checkMangerInTeam(ProjectVO projectVO) throws BusinessException {

        if(projectVO!=null){
            if(StringUtils.isEmpty(projectVO.getManager())){
                return;
            }

            TeamMemberVO[] teamMemberVOs = memberQuery.queryByProAndPkUsers(projectVO.getPkProject(),new String[] {projectVO.getManager()});
            if (ArrayUtils.isEmpty(teamMemberVOs)){
                throw new BusinessException("项目经理不可移除出项目团队！");
            }
            if(teamMemberVOs.length > 1){
                throw new BusinessException("项目团队中存在重复人员：项目经理");
            }
            if(TeamMemberVO.UserRoleConst.ROLE_MANAGER != teamMemberVOs[0].getUserRole() ){
                throw new BusinessException("项目经理不可移除出项目团队！");
            }
        }
    }

    /**
     * 更新工序与项目之间的关系信息
     */
    private void updateProcedure(String pkProject,String[] pkProcedures) throws BusinessException {
        SingleInsertBpTemplate<ProceprojectVO> bp = new SingleInsertBpTemplate<ProceprojectVO>(proceprojectMapper);
            proceprojectMapper.deleteByProjectId(pkProject);
            if (ArrayUtils.isNotEmpty(pkProcedures)){
                for (String pkProcedure:pkProcedures){
                    ProceprojectVO proceprojectVO = new ProceprojectVO();
                    proceprojectVO.setPkProcedure(pkProcedure);
                    proceprojectVO.setPkProject(pkProject);
                    bp.addBeforeProcessor(new AuditInfoInsertProcessor<ProceprojectVO>());
                    bp.insert(proceprojectVO);
                }
            }
    }


    /**
     * 更新项目参与方信息
     *
     * @param pkProject
     * @param projectUpdateViewVO
     * @throws BusinessException
     */
    private void updateTeamEnter(String pkProject, ProjectUpdateViewVO projectUpdateViewVO) throws BusinessException {
        ProjectUpdateViewVO.EnterpriseUpdateInfo enterpriseUpdateInfo = projectUpdateViewVO.getEnterpriseUpdateInfo();
        if (enterpriseUpdateInfo != null) {
            teamEnterpriseMaintain.updateEnterpriseForProject(pkProject, enterpriseUpdateInfo.getInsertEnterpriseVOs(), enterpriseUpdateInfo.getUpdateEnterpriseVOs(), enterpriseUpdateInfo.getDeleteEnterpriseVOs());
        }
    }

    /**
     * 更新团队成员信息
     *
     * @param pkProject
     * @param projectUpdateViewVO
     * @throws BusinessException
     */
    private void updateTeamMember(String pkProject, ProjectUpdateViewVO projectUpdateViewVO) throws BusinessException {
        ProjectUpdateViewVO.MemberUpdateInfo memberUpdateInfo = projectUpdateViewVO.getMemberUpdateInfo();
        ProjectUpdateViewVO.EnterpriseUpdateInfo enterpriseUpdateInfo = projectUpdateViewVO.getEnterpriseUpdateInfo();
        if (memberUpdateInfo != null) {
            memberMaintain.updateTeamMemberForProject(pkProject, memberUpdateInfo.getUpdateMemberVOs(), memberUpdateInfo.getInsertMemberVOs(), memberUpdateInfo.getDeleteMemberVOs(), enterpriseUpdateInfo.getInsertEnterpriseVOs(), enterpriseUpdateInfo.getUpdateEnterpriseVOs());
        }
    }
}
